package com.zenob.secalendar.util;

import java.util.Calendar;
import java.util.Date;

/**
 * TODO:
 * - To tell all-day events from events starting at midnight, at 1 millisecond in isoDateToLong if time is not given.
 * Return date with no time if hour=0, min=0, sec=0, millis=1.
 * - Better: new method longIntervalToIsoDateInterval. If startDate=endDate, skip time. If hour=min=sec=millis=0 for both startdate and endDate, skip time. Else include time.
 *
 * @author Thomas Oldervoll, thomas@zenior.no
 * @author $Author$
 * @version $Rev: 40 $
 * @date $Date$
 */
public class DateUtil {

    public final static long DAY = 24 * 3600 * 1000;
    private final static int ALL_DAY_MARKER = 1;

	public static String dateToIsoDateTime(String date)
	{
		StringBuffer sb = new StringBuffer(date);

		if (date.length() >= 8)
		{
			//20070101 -> 2007-01-01
			//insert hyphens between year, month, and day
			sb.insert(4, '-');
			sb.insert(7, '-');

			//2007-01-01T173012 -> 2007-01-01T17:30:12
			if (date.length() >= 15)
			{
				//insert colons between hours, minutes, and seconds
				sb.insert(13, ':');
				sb.insert(16, ':');

				//2007-01-01T17:30:12 -> 2007-01-01T17:30:12.000
				//append milliseconds
				if (sb.toString().length() > 19)
					sb.insert(20, ".000");
				else
					sb.append(".000");

				//2007-01-01T17:30:12.000 -> 2007-01-01T17:30:12.000Z
				//append UTC indicator if exists
				if (date.length() > 15 && date.charAt(15) == 'Z')
					sb.append('Z');
			}

			if (sb.length() > 19) sb.setLength(19);
		}

		return sb.toString();
	}

	public static String isoDateTimeToDate(String isoDate)
	{
		StringBuffer sb = new StringBuffer(isoDate);
		int maxlen;

		if (isoDate.length() >= 10)
		{
			//2007-01-01 -> 20070101
			//remove hyphens between year, month, and day
			sb.deleteCharAt(4);
			sb.deleteCharAt(6);

			maxlen = 8;

			//20070101T17:30:12 -> 20070101T173012
			if (isoDate.length() >= 19)
			{
				maxlen = 15;

				//remove colons between hours, minutes, and seconds
				sb.deleteCharAt(11);
				sb.deleteCharAt(13);

				if (isoDate.indexOf("Z") >= 0)
				{	
					sb.insert(15, 'Z');
					++maxlen;
				}
			}
			if (sb.length() > maxlen) sb.setLength(maxlen);
		}

		return sb.toString();
	}

	public static String longToDate(long longDate)
	{
		String str = longToIsoDate(longDate);
		return isoDateTimeToDate(str);
	}

	public static String longToDateTime(long longDate)
	{
		String str = longToIsoDateTime(longDate);
		return isoDateTimeToDate(str);
	}

	public static long dateToLong(String date)
	{
		String str = dateToIsoDateTime(date);
		return isoDateToLong(str);
	}

    public static long isoDateToLong(String isoDate) {
        String year = isoDate.substring(0, 4);
        String month = isoDate.substring(5, 7); 
        String day = isoDate.substring(8, 10);  
        String hour = "0";
        String min = "0";
        String sec = "0";
        String millis = Integer.toString(ALL_DAY_MARKER);
        String timeZoneHour = "+00";
        String timeZoneMin = "00";
		boolean timeIncluded = false;
		long timeZoneAdjustment = 0;

        if (isoDate.length() >= 11) {
            if ((isoDate.charAt(10) == 'T') && isoDate.length() >= 23) {
                // Time of day given. Example: 2006-04-24T16:29:59.001
                hour = isoDate.substring(11, 13);
                min = isoDate.substring(14, 16);
                sec = isoDate.substring(17, 19);
                millis = isoDate.substring(20, 23);
                if (isoDate.length() >= 29) {
                    // Time of day and timezone given. Example: 2006-04-24T16:29:59.001-07:00
                    timeZoneHour = isoDate.substring(23, 26);
                    timeZoneMin = isoDate.substring(27, 29);
                }

				timeIncluded = true;

            } else {
                // No time of day but timezone given. Example: 2006-04-24-07:00
                if (isoDate.length() >= 16) {
                    timeZoneHour = isoDate.substring(10, 13);
                    timeZoneMin = isoDate.substring(14, 16);
                }
            }
        }
        // else: no time of day, no timezone. Example: 2006-04-24

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, Integer.parseInt(year));
        calendar.set(Calendar.MONTH, Integer.parseInt(month) - 1); // January is 0
        calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day));
        calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour));
        calendar.set(Calendar.MINUTE, Integer.parseInt(min));
        calendar.set(Calendar.SECOND, Integer.parseInt(sec));
        calendar.set(Calendar.MILLISECOND, Integer.parseInt(millis));

        //System.out.println("Time without timezone: " + calendar.getTime());

		//adjust time if it was included
        
        /*
        if (timeIncluded) 
		{
			Options options = Store.getOptions();
			timeZoneAdjustment = options.downloadTimeZoneOffset;
			if (options.useGCalTimeZone) {
				timeZoneAdjustment -= stringTimeZoneToLong(timeZoneHour, timeZoneMin);
			}
		}
		*/

        long timeMillis = calendar.getTime().getTime() + timeZoneAdjustment;

        //System.out.println("Time is: " + new Date(timeMillis));
        return timeMillis;
    }

    private static long stringTimeZoneToLong(String timeZoneHour, String timeZoneMin) {
        return  parseSignedInt(timeZoneHour) * 3600 * 1000 + Integer.parseInt(timeZoneMin) * 60 * 1000;
    }

    private static int parseSignedInt(String value) {
        int sign = 1;
        if (value.startsWith("-")) {
            sign = -1;
        }
        int absoluteValue = Integer.parseInt(value.substring(1));
        return sign * absoluteValue;
    }

    public static String[] longIntervalToIsoDateInterval(long[] longInterval) {
        String startDate = null;
        String endDate = null;
        boolean includeTime = true;
        if (longInterval[0] == longInterval[1]) {
            // all-day event (according to javax.microedition.pim.Event JavaDoc, this is how a all-day event is specified)
            startDate = longToIsoDate(longInterval[0]);
            endDate = startDate;
        } else {
            Calendar startCalendar = Calendar.getInstance();
            startCalendar.setTime(new Date(longInterval[0]));
            Calendar endCalendar = Calendar.getInstance();
            startCalendar.setTime(new Date(longInterval[1]));
            if (isMidnight(startCalendar) && isMidnight(endCalendar)) {
                // all-day event, multiple days
                includeTime = false;
            }
            startDate = longToIsoDateTime(longInterval[0], includeTime);
            endDate = longToIsoDateTime(longInterval[1], includeTime);
        }
        return new String[]{startDate, endDate};
    }

    private static boolean isMidnight(Calendar calendar) {
        return (calendar.get(Calendar.HOUR_OF_DAY) == 0) &&
                (calendar.get(Calendar.MINUTE) == 0) &&
                (calendar.get(Calendar.SECOND) == 0) &&
                (calendar.get(Calendar.MILLISECOND) == 0);
    }

    public static String longToIsoDate(long longDate, int offsetDays) {
        return longToIsoDate(longDate + offsetDays * DAY);
    }

    public static String longToIsoDate(long longDate) {
        return longToIsoDateTime(longDate, false);
    }

    public static String longToIsoDateTime(long longDate) {
        return longToIsoDateTime(longDate, true);
    }

    private static String longToIsoDateTime(long longDate, boolean includeTime) {
        Calendar calendar = Calendar.getInstance();
        
        /*
        if (includeTime) {
            longDate += Store.getOptions().uploadTimeZoneOffset;
        }   
         */
        calendar.setTime(new Date(longDate));
        return calendarToIsoDateTime(calendar, includeTime);
    }

    private static String calendarToIsoDateTime(Calendar calendar, boolean includeTime) {
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        if (!includeTime) {
            return year + "-" + twoDigit(month) + "-" + twoDigit(day);
        } else {
            int hour = calendar.get(Calendar.HOUR_OF_DAY);
            int min = calendar.get(Calendar.MINUTE);
            int sec = calendar.get(Calendar.MINUTE);
            int millis = calendar.get(Calendar.MILLISECOND);
            if (millis == ALL_DAY_MARKER) {
                return year + "-" + twoDigit(month) + "-" + twoDigit(day);
            } else {
                return year + "-" + twoDigit(month) + "-" + twoDigit(day)
                        + "T" + twoDigit(hour) + ":" + twoDigit(min) + ":" + twoDigit(sec) + "." + threeDigit(millis);
            }
        }
    }

    private static String twoDigit(int value) {
        if (value < 10) {
            return "0" + value;
        } else {
            return "" + value;
        }
    }

    private static String threeDigit(int value) {
        if (value < 10) {
            return "00" + value;
        } else if (value < 100) {
            return "0" + value;
        } else {
            return "" + value;
        }
    }

    /*
    private void computeFields(Calendar calendar) {
        if (calendar instanceof CalendarImpl) {

        }
    }
    */

}

